import { Helper } from '../tool/helper';
import * as BL from '../index';


/**
 * 舞台资源信息对象接口
 */
interface IStageAssetsInfo {
    sceneIndex: number,
    assetTaskInfos: BL.ILoose<BL.AbstractAssetTaskInfo>,
}


/**
 * 舞台
 */
abstract class Stage {

    private _enabled: boolean = false;
    private _prepared: boolean = false;
    private _loaders: BL.AssetsLoad[] = [];

    protected scenes!: Array<BL.Scene>;
    protected camreas!: Array<BL.Camera>;
    protected lights!: Array<BL.Light>;

    public readonly engine: BL.Engine;
    public readonly canvas: HTMLCanvasElement;
    public readonly bufferLoader: BL.BufferLoad
    public name: string = '';
    public id: string = BL.RandomGUID();
    public onUserGestureObservable: BL.Observable<boolean> = new BL.Observable();
    public onWindowResizeObservable: BL.Observable<DOMRect> = new BL.Observable();
    public onActiveObservable: BL.Observable<boolean> = new BL.Observable();
    public onPreparedObservable: BL.Observable<void> = new BL.Observable();
    public onReady?: { (): void };
    public autoHideLoadingUI = true;

    constructor(engine: BL.Engine, canvas: HTMLCanvasElement, bufferLoader: BL.BufferLoad) {
        this.engine = engine;
        this.canvas = canvas;
        this.bufferLoader = bufferLoader;
        this.scenes = this.initializeScenes();
        this.camreas = this.initializeCameras(this.scenes);
        this.lights = this.initializeLights(this.scenes, this.camreas);
    }

    public get uiScene(): BL.Scene {
        return this.scenes[0];
    }

    public get mainScene(): BL.Scene {
        return this.scenes[0];
    }

    public get uiCamera(): BL.Camera {
        return this.camreas[0];
    }

    public get mainCamera(): BL.Camera {
        return this.camreas[0];
    }

    /**
     * 舞台初始化是否完成
     */
    public get prepared(): boolean {
        return this._prepared;
    }

    /**
     * 舞台是否启用
     */
    public get enable(): boolean {
        return this._enabled;
    }

    /**
     * 启用舞台
     */
    public set enable(enabled: boolean) {
        this._enabled = enabled;
        if (this._enabled) {
            if (!this._prepared) {
                this.loadAssets();
            } else {
                this.onEnable(false);
                this.onActiveObservable.notifyObservers(true);
            }

        } else {
            this.onDisable();
            this.onActiveObservable.notifyObservers(false);
        }
    }

    /**
     * 获取资源加载组件
     * @param scene 资源加载的依赖场景
     * @returns 资源加载组件
     */
    public loader(scene?: BL.Scene): BL.AssetsLoad {
        const scn = scene || this.scenes[0];
        for (const ldr of this._loaders) {
            if (ldr.scene.uid === scn.uid) return ldr;
        }
        const ldr = new BL.AssetsLoad(scn);
        this._loaders.push(ldr);
        return ldr;
    }

    /**
     * 循环渲染
     */
    public render(): void {
        for (const scene of this.scenes) {
            scene.render();
        }
    }

    /**
     * 重置场景
     */
    public reset(): void { }

    /**
     * 设置视角（视角重置、切换等）
     */
    public resetSight(): void { }

    /**
     * 窗体变化时执行
     * @param rect 矩形尺寸
     */
    public onWindowResize(rect: DOMRect): void { }

    /**
     * 用户行为
     * @param debut 首次
     */
    public onUserGesture(debut: boolean): void { }

    /**
     * 初始化场景
     * @returns 场景集合
     */
    protected initializeScenes(): Array<BL.Scene> {
        const scene = BL.BabylonContainer.createScene()!;
        return [scene];
    }

    /**
     * 初始化相机
     * @param scenes 场景集合
     * @returns 相机集合
     */
    protected initializeCameras(scenes: Array<BL.Scene>): Array<BL.Camera> {
        const camera = new BL.ArcRotateCamera('defaultCamera', Math.PI * 0.5, 1.25, 10, BL.Vector3.Zero(), scenes[0]);
        camera.attachControl(this.canvas, false);
        Helper.markGeneralCamera(camera);
        scenes[0].activeCamera = camera;
        camera.minZ = 0.1;
        camera.maxZ = 100;
        camera.lowerRadiusLimit = 2;
        camera.upperRadiusLimit = 20;
        camera.wheelDeltaPercentage = 0.01;
        camera.pinchDeltaPercentage = 0.001;
        camera.panningSensibility = 1000;
        camera.angularSensibilityX = 2000;
        camera.angularSensibilityY = 2000;
        return [camera];
    }

    /**
     * 初始化灯光
     * @param scenes 场景集合
     * @param cameras 相机集合
     * @returns 灯光集合
     */
    protected initializeLights(scenes: Array<BL.Scene>, cameras: Array<BL.Camera>): Array<BL.Light> {
        const ambLight = new BL.HemisphericLight('ambient', new BL.Vector3(1, 2, 0.5), scenes[0]);
        ambLight.diffuse = BL.Color3.FromHexString('#ffffff');
        ambLight.specular = BL.Color3.FromHexString('#ffffff');
        ambLight.groundColor = BL.Color3.FromHexString('#707070');
        ambLight.intensity = 0.85;
        return [ambLight];
    }

    /**
     * 设置舞台资源信息
     * @returns 资源任务信息对象，或舞台资源信息集合
     */
    protected assetTaskInfos(): BL.ILoose<BL.AbstractAssetTaskInfo> | Array<IStageAssetsInfo> {
        return [];
    }

    /**
     * 加载资源
     */
    private loadAssets(): boolean {
        this.engine.loadingScreen.displayLoadingUI();
        const astTask = this.assetTaskInfos();
        const assets: IStageAssetsInfo[] = Array.isArray(astTask) ? astTask : [{ sceneIndex: 0, assetTaskInfos: astTask }];
        const initialize = async () => {
            this._prepared = true;
            this.onPreparedObservable.notifyObservers();
            await this.initializeComponents(this.scenes, this.camreas, this.lights);
            this.initializeInput(this.scenes, this.camreas);

            this.onEnable(true);
            this.onActiveObservable.notifyObservers(true);
            await BL.Utils.sleep(0);
            let c = 0;
            for (const scene of this.scenes) {
                await BL.Utils.sleep(0);
                scene.executeWhenReady(() => {
                    c++;
                    if (c >= this.scenes.length) {
                        if (this.autoHideLoadingUI) this.engine.loadingScreen.hideLoadingUI();
                        if (this.onReady) this.onReady();
                    }
                }, false);
            }
        };
        if (assets.length == 0) {
            initialize();
            return true;
        }
        for (let i = 0; i < assets.length; i++) {
            const ast = assets[i];
            const idx = typeof ast.sceneIndex !== 'number' ? 0 : ast.sceneIndex;
            const loader = this.loader(this.scenes[idx]);
            loader.load(ast.assetTaskInfos,
                () => { },
                () => { },
                () => { },
                () => {
                    let total = 0;
                    for (const ldr of this._loaders) {
                        total += ldr.progress;
                    }

                    let astCount = 0;
                    for (const asset of assets) {
                        if (Object.values(asset.assetTaskInfos).length) astCount++;
                    }

                    if (Math.floor(total + 1e-6) < Math.round(astCount)) return;
                    initialize();
                },
                () => { }
            );
        }

        return true;
    }

    /**
     * 舞台启用时执行
     * @param debut 是否首次启用
     */
    protected onEnable(debut: boolean): void { }

    /**
     * 舞台禁用时执行
     */
    protected onDisable(): void { }

    /**
     * 初始化业务组件
     * @param scenes 场景集合
     * @param cameras 相机集合
     * @param lights 灯光集合
     */
    protected async initializeComponents(scenes: Array<BL.Scene>, cameras: Array<BL.Camera>, lights: Array<BL.Light>): Promise<void> { }

    /**
     * 初始化输入控制
     * @param scenes 场景集合
     * @param cameras 相机集合
     */
    protected initializeInput(scenes: Array<BL.Scene>, cameras: Array<BL.Camera>): void { }

}

/**
 * 舞台比较函数
 * @param a 舞台
 * @param b 舞台
 * @returns 是否相同
 */
const COMPARE_STAGE = (a: Stage, b: Stage) => {
    return a.id === b.id;
};

export { Stage, COMPARE_STAGE };
export type { IStageAssetsInfo };